// This file is a part of Funky C++.
// Copyright (C) 2011  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifndef MEAN_BANDS_HH
# define MEAN_BANDS_HH

# include "signal.hh"

namespace funky {
  template <typename Signal, typename Sample, size_t N, size_t Size>
  struct mean_bands_sig {
  private:
    Signal signal;

  public:
    template <typename S>
    explicit mean_bands_sig(S&& s):
      signal{std::forward<S>(s)} {}

    mean_bands_sig(const mean_bands_sig&) = default;
    mean_bands_sig(mean_bands_sig&& other):
      signal(std::move(other.signal)) {}

    mean_bands_sig& operator=(const mean_bands_sig& other) {
      return *this = mean_bands_sig(other);
    }

    mean_bands_sig& operator=(mean_bands_sig&& other) {
      std::swap(signal, other.signal);
      return *this;
    }

    mean_bands_sig& operator++() {
      ++signal;
      return *this;
    }

    mean_bands_sig& operator++(int) {
      mean_bands_sig ret{*this};
      ++*this;
      return ret;
    }

    bus<Sample, N> operator*() const {
      bus<Sample, N> ret{*signal};
      for (size_t i = 0; i < (N/Size); ++i) {
	for (size_t j = 1; j < Size; ++j) {
	  ret[i*Size] += ret[i*Size+j];
	}
	ret[i*Size] /= Size;
	for (size_t j = 1; j < Size; ++j) {
	  ret[i*Size+j] = ret[i*Size];
	}
      }
      return ret;
    }
  };

  template <typename Signal, typename Sample, size_t N, size_t Size>
  struct signal_traits<mean_bands_sig<Signal, Sample, N, Size> > {
    typedef bus<Sample, N> sample;
    typedef size_t duration;
  };

  template <size_t Size,
	    typename T, typename RT = typename std::decay<T>::type,
	    typename Sample = typename signal_traits<RT>::sample,
	    typename BusSample = typename Sample::sample,
	    size_t BusSize = Sample::size>
  mean_bands_sig<RT, BusSample, BusSize, Size> mean_bands(T&& t) {
    return mean_bands_sig<RT, BusSample, BusSize, Size>{
      std::forward<T>(t)};
  }
}

#endif
